/*
 * Copyright (c) 2006-2009 by Abacus Research AG, Switzerland.
 * All rights reserved.
 *
 * This file is part of the Abacus Formula Compiler (AFC).
 *
 * For commercial licensing, please contact sales(at)formulacompiler.com.
 *
 * AFC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * AFC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with AFC.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.formulacompiler.compiler.internal.expressions.parser;

import java.util.Collection;
import java.util.List;
import java.util.Stack;

import org.formulacompiler.compiler.CompilerException;
import org.formulacompiler.compiler.internal.expressions.ExpressionNode;
import org.formulacompiler.runtime.New;


/**
 * Provides a node stack for the recursive descent expression parser generated by JavaCC. Also adds
 * override points for implementing spreadsheet-specific elements like cells and ranges. Those are
 * not implemented here so the basic parser does not depend on
 * {@link org.formulacompiler.spreadsheet}. Nevertheless, it does "parse" the cell constructs
 * because it seems impossible to add them in derivations (JavaCC does not support inheritance and
 * it would be hard for it to do so, too).
 * 
 * @author peo
 */
abstract class ExpressionParserBase
{
	/**
	 * Use a List instead of a Deque here so {@link #popNodes(int)} does not have to reverse
	 * elements.
	 */
	private final List<ExpressionNode> nodes = New.list();

	protected final void pushNode( ExpressionNode _node )
	{
		this.nodes.add( _node );
	}

	protected final ExpressionNode popNode()
	{
		return this.nodes.remove( this.nodes.size() - 1 );
	}

	/**
	 * Returns the top <i>n</i> nodes <em>in the order they were pushed</em> (FIFO). Popping the
	 * nodes individually would reverse this order (LIFO).
	 */
	protected final List<ExpressionNode> popNodes( int _howMany )
	{
		final int have = this.nodes.size();
		if (_howMany > have) {
			throw new IllegalArgumentException( "_howMany > have" );
		}
		else {
			final List<ExpressionNode> result = New.list( _howMany );
			for (int i = have - _howMany; i < have; i++) {
				result.add( this.nodes.get( i ) );
			}
			for (int i = 1; i <= _howMany; i++) {
				this.nodes.remove( have - i );
			}
			return result;
		}
	}


	private final Stack<Integer> marks = new Stack<Integer>();

	/**
	 * Remembers the current node stack pointer for {@code popMarkedNodes()}. Calls are nestable.
	 */
	protected final void mark()
	{
		this.marks.push( this.nodes.size() );
	}

	/**
	 * Returns the top <i>n</i> nodes since the last {@code mark()}
	 * <em>in the order they were pushed</em> (FIFO).
	 */
	protected final List<ExpressionNode> popMarkedNodes()
	{
		final int mark = this.marks.pop();
		return popNodes( this.nodes.size() - mark );
	}


	/**
	 * Pops the top node and adds it as the last child to the then top node.
	 */
	protected final void popNodeAndMergeIntoTopNode()
	{
		final ExpressionNode popped = popNode();
		final ExpressionNode top = peekNode();
		top.addArgument( popped );
	}

	protected final ExpressionNode peekNode()
	{
		return this.nodes.get( this.nodes.size() - 1 );
	}


	protected final Number parseInt( String _text )
	{
		return Double.parseDouble( _text ); // LATER return proper data type
	}

	protected final Number parseDouble( String _text )
	{
		return Double.parseDouble( _text ); // LATER return proper data type
	}

	protected final String parseQuotedString( String _text )
	{
		return _text.substring( 1, _text.length() - 1 ).replaceAll( "\"\"", "\"" );
	}


	protected final void unsupportedFunction( Token _name )
	{
		throw new InnerParserException( new CompilerException.UnsupportedExpression( "Unsupported function "
				+ _name.image + " encountered" ) );
	}


	protected abstract CellRefFormat getCellRefFormat();

	protected abstract Object makeCell( Token _cell, Object _baseCell );

	protected abstract boolean isRangeName( Token _name );

	protected abstract ExpressionNode makeNamedRangeRef( Token _name );

	protected abstract Object makeCellRange( Object _from, Object _to );

	protected abstract Object makeCellRange( Token _range );

	protected abstract void convertRangesToCells( boolean _allowRanges );

	protected abstract ExpressionNode makeNodeForReference( Object _reference );

	protected abstract ExpressionNode makeRangeIntersection( Collection<ExpressionNode> _firstTwoElements );

	protected abstract ExpressionNode makeRangeUnion( Collection<ExpressionNode> _firstTwoElements );

	protected abstract ExpressionNode makeShapedRange( ExpressionNode _range );


	protected void makeNewRuleDef( Token _name )
	{
		throw new IllegalStateException();
	}

	protected void finalizeLastRuleDef()
	{
		throw new IllegalStateException();
	}

	protected void makeNewFoldDef( Token _name )
	{
		throw new IllegalStateException();
	}

	protected void finalizeLastFoldDef()
	{
		throw new IllegalStateException();
	}

	protected void makeNewParam( Token _name, char _suffix )
	{
		throw new IllegalStateException();
	}

	protected void let( Token... _name )
	{
		throw new IllegalStateException();
	}

	protected void unlet( Token... _name )
	{
		throw new IllegalStateException();
	}

	protected void letParams()
	{
		throw new IllegalStateException();
	}

	protected void unletParams()
	{
		throw new IllegalStateException();
	}

	protected void makeBody()
	{
		throw new IllegalStateException();
	}


	protected void initFold()
	{
		throw new IllegalStateException();
	}

	protected void addFoldAccuInit( Token _name, ExpressionNode _init )
	{
		throw new IllegalStateException();
	}

	protected void addFoldEltName( Token _name )
	{
		throw new IllegalStateException();
	}

	protected void setFoldIdxName( Token _name )
	{
		throw new IllegalStateException();
	}

	protected void letFoldAccus()
	{
		throw new IllegalStateException();
	}

	protected void unletFoldAccus()
	{
		throw new IllegalStateException();
	}

	protected void letFoldElts()
	{
		throw new IllegalStateException();
	}

	protected void unletFoldElts()
	{
		throw new IllegalStateException();
	}

	protected void letFoldCount()
	{
		throw new IllegalStateException();
	}

	protected void unletFoldCount()
	{
		throw new IllegalStateException();
	}

	protected void addFoldStep( Token _name, ExpressionNode _step )
	{
		throw new IllegalStateException();
	}

	protected void setFoldCountName( Token _name )
	{
		throw new IllegalStateException();
	}

	protected void setFoldInto( ExpressionNode _node )
	{
		throw new IllegalStateException();
	}

	protected void setFoldWhenEmpty( ExpressionNode _node )
	{
		throw new IllegalStateException();
	}

	protected void pushFold( boolean _mayRearrange, boolean _mayReduce )
	{
		throw new IllegalStateException();
	}

	protected void pushApplyList( Token _def, Token _elts )
	{
		throw new IllegalStateException();
	}

	protected void pushApplyVectors( Token _def, Collection<Token> _vecs )
	{
		throw new IllegalStateException();
	}

	protected void setExprType( final Token _type )
	{
		throw new IllegalStateException();
	}


	protected static final class InnerParserException extends RuntimeException
	{
		public InnerParserException( Throwable _cause )
		{
			super( _cause );
		}
	}

}
